﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CarEvaluationANN
{
    /// <summary>
    /// Object representation of a csv file
    /// </summary>
    public class CSVTable
    {
        #region Props&Fields&Indexers

        //Wrapped to prevent coding issues
        private List<CSVRow> _table = new List<CSVRow>();

        private List<CSVRow> Table
        {
            get { return _table; }
            set { _table = value; }
        }

        public CSVRow HeaderRow
        {
            get { return Table[0]; }
        }

        public string this[int rowIndex, string columnName]
        {
            get
            {
                int colIndex = GetColumnIndex(columnName);
                return Table[rowIndex][colIndex];
            }
            set
            {
                int colIndex = GetColumnIndex(columnName);
                Table[rowIndex][colIndex] = value;
            }
        }

        public int ColumnCount
        {
            get { return HeaderRow.Count; }
        }

        public int RowCount
        {
            get { return Table.Count; }
        }

        /// <summary>
        /// Content without header row
        /// </summary>
        public CSVTable Content
        {
            get { return new CSVTable(Table.GetRange(1, Table.Count - 1)); }
        }

        #endregion

        #region Constructors

        public CSVTable(string content)
        {
            InitializeList(content);
        }

        public CSVTable(IEnumerable<string> strArray)
        {
            InitializeList(strArray);
        }

        public CSVTable(IEnumerable<CSVRow> rowArray)
        {
            Table.AddRange(rowArray);
        }

        #endregion

        #region Methods

        private int GetColumnIndex(string columnName)
        {
            int colIndex = HeaderRow.FindIndex(columnName);
            if (colIndex < 0)
            {
                throw new Exception(String.Format("Specified csv column <{0}> does not exist!!!", columnName));
            }
            return colIndex;
        }

        public IEnumerable<string> GetColumn(string columnName)
        {
            var column = new List<string>();
            int colIndex = GetColumnIndex(columnName);
            Table.ForEach(row => column.Add(row[colIndex]));
            column.RemoveAt(0);
            return column;
        }

        private void InitializeList(string content)
        {
            List<string> arr = content.Split(new[] {Environment.NewLine}, StringSplitOptions.None).ToList();
            InitializeList(arr);
        }

        private void InitializeList(IEnumerable<string> content)
        {
            foreach (var row in content)
            {
                if (String.IsNullOrEmpty(row))
                    continue;
                Table.Add(new CSVRow(row));
            }
            if (Table.Exists(r => r.Count != HeaderRow.Count))
            {
                throw new Exception("Content to parse can not be converted to rectangular array!!!");
            }
        }

        public void AddColumn(string columnName)
        {
            if (!ContainsColumn(columnName))
            {
                HeaderRow.Add(columnName);
                Content.Table.ForEach(row => row.Add(String.Empty));
            }
        }

        public void AddRow(CSVRow row)
        {
            if (row.Count != ColumnCount)
                throw new Exception("Row column count has to be equal to column count!!!");
            Table.Add(row);
        }

        public bool ContainsColumn(string columnName)
        {
            return HeaderRow.Exists(columnName);
        }

        public override string ToString()
        {
            var strBuilder = new StringBuilder();
            Table.ForEach(r => strBuilder.AppendLine(r.ToString()));
            return strBuilder.ToString();
        }

        #endregion
    }

    /// <summary>
    /// Object representation of a csv row
    /// </summary>
    public class CSVRow
    {
        #region Props&Fields

        private const string CSVRowFormat = @"=""{0}""";
        private List<string> _row = new List<string>();

        public string this[int index]
        {
            get { return TrimCell(Row[index]); }
            set { Row[index] = String.Format(CSVRowFormat, value); }
        }

        public int Count
        {
            get { return Row.Count; }
        }

        private List<string> Row
        {
            get { return _row; }
            set { _row = value; }
        }

        #endregion

        #region Constructors

        public CSVRow(IEnumerable<string> collection)
        {
            Row.AddRange(collection);
        }

        public CSVRow(string rawString)
        {
            InitializeRow(rawString);
        }

        #endregion

        #region Methods

        private void InitializeRow(string rawString)
        {
            string[] cells = rawString.Split(';');
            for (int i = 0; i < cells.Length; i++)
            {
                cells[i] = String.Format(CSVRowFormat, cells[i]);
            }
            Row.AddRange(cells);
        }

        public void Add(string cellValue)
        {
            Row.Add(String.Format(CSVRowFormat, cellValue));
        }

        public override string ToString()
        {
            return String.Join(";", Row.ToArray());
        }

        public int FindIndex(string value)
        {
            return Row.FindIndex(c => TrimCell(c) == value);
        }

        public bool Exists(string value)
        {
            return Row.Exists(c => TrimCell(c) == value);
        }

        private string TrimCell(string s)
        {
            return s.TrimStart('=').Trim('"').Trim();
        }

        public IEnumerable<string> GetRow()
        {
            return Row.Select(TrimCell).ToList();
        }

        #endregion
    }
}